#include <string>
#include <vector>

#include "gtest\gtest.h"
#include "Keyboard Warrior\Logic.h"
#include "Keyboard Warrior\Parser.h"
#include "Keyboard Warrior\TaskList.h"
#include "Keyboard Warrior\History.h"
#include "Keyboard Warrior\Date.h"
#include "Keyboard Warrior\Time.h"


//@author A0105566E
TEST(Logic,searchFunctionSingleResult)
{
	//std::vector<std::string> v;
	std::string result;


	Task *task = new FloatingTask("floating");
	TaskList::addTask(task);
	
	Logic::searchByDesc("floating");
	Logic::displaySearchResults(result);
	EXPECT_EQ("The results are:\n\n1. floating\n",result);
}

TEST(Logic,searchFunctionMultipleResult)
{
	//std::vector<std::string> v;
	std::string result;
	Logic::resetBuffer();
	//TaskList::resetTask();
	Task *task2 = new FloatingTask("floating task");
	TaskList::addTask(task2);
	Logic::searchByDesc("floating");
	Logic::displaySearchResults(result);
	EXPECT_EQ("The results are:\n\n1. floating\n2. floating task\n",result);
}

TEST(Logic, deleteFunctionMultipleMatch){
	std::vector<std::string> v;
	v.push_back("delete");
	v.push_back("floating");
	string result;
	Logic::deleteTask(v,result);
	EXPECT_EQ("The candidate solutions are: \n1. floating\n2. floating task\n",result);
}

TEST(Logic, deleteFunctionMultipleMatchDelete){
	std::vector<std::string> v;
	v.push_back("delete");
	v.push_back("1");
	string result;
	Logic::deleteTask(v,result);
	EXPECT_EQ(MESSAGE_DELETE_SUCCESS,result);
}

//@author A0099619J
TEST(Parser, splitString) {
	std::vector<std::string> v;
	std::string str = "add project meeting, 2pm";
	Parser::splitString(str, v);

	ASSERT_EQ(3, v.size());
	EXPECT_EQ("add", v[0]);
	EXPECT_EQ("project meeting", v[1]);
	EXPECT_EQ("2pm", v[2]);
}

TEST(Parser, splitEmptyString) {
	std::vector<std::string> v;
	std::string str = "";
	Parser::splitString(str, v);

	ASSERT_EQ(1, v.size());
	EXPECT_EQ("", v[0]);
}

TEST(Parser, splitSingleWord) {
	std::vector<std::string> v;
	std::string str = "test";
	Parser::splitString(str, v);

	ASSERT_EQ(1, v.size());
	EXPECT_EQ("test", v[0]);
}

TEST(Parser, splitSingleWordWithSpaces) {
	std::vector<std::string> v;
	std::string str = "test     ";
	Parser::splitString(str, v);

	ASSERT_EQ(1, v.size());
	EXPECT_EQ("test", v[0]);
}

TEST(Parser, parseDate) {
	Date date;
	// Abbreviated month and year
	date = Parser::parseDate("14 Oct 13");
	EXPECT_EQ(Date(14,10,13), date);
	// Full month and year
	date = Parser::parseDate("11 November 2011");
	EXPECT_EQ(Date(11,11,11), date);
	// 29 February for leap year
	date = Parser::parseDate("29 Feb 2016");
	EXPECT_EQ(Date(29,02,16), date);
}

TEST(Parser, invalidDate) {
	Date date;
	// Random word
	date = Parser::parseDate("pineapple");
	EXPECT_EQ(NULL_DATE, date);
	// Invalid day of month
	date = Parser::parseDate("31 April 2013");
	EXPECT_EQ(NULL_DATE, date);
	// 29 February for non-leap year
	date = Parser::parseDate("29 February 2013");
	EXPECT_EQ(NULL_DATE, date);
}

TEST(Parser, parseTime) {
	Time time;
	// Hour only with PM
	time = Parser::parseTime("2PM");
	EXPECT_EQ(Time(14,0), time);
	// Hour, minutes with colon separator
	time = Parser::parseTime("11:59 PM");
	EXPECT_EQ(Time(23,59), time);
	// Hour, minute without colon and PM
	time = Parser::parseTime("1800");
	EXPECT_EQ(Time(18,0), time);
}

TEST(Parser, invalidTime) {
	Time time;
	// Empty string
	time = Parser::parseTime("");
	EXPECT_EQ(NULL_TIME, time);
	// Random word
	time = Parser::parseTime("pineapple");
	EXPECT_EQ(NULL_TIME, time);
	// Invalid minute
	time = Parser::parseTime("1460");
	EXPECT_EQ(NULL_TIME, time);
}

TEST(Date, comparisonOperators)
{
	Date date1(10, 11, 11), date2(11, 11, 11);
	EXPECT_EQ(true, date1 <= date1);
	EXPECT_EQ(true, date1 >= date1);
	EXPECT_EQ(true, date1 == date1);
	EXPECT_EQ(false, date2 < date2);
	EXPECT_EQ(false, date2 > date2);
	EXPECT_EQ(false, date2 != date2);

	EXPECT_EQ(true, date1 < date2);
	EXPECT_EQ(true, date1 <= date2);
	EXPECT_EQ(false, date1 > date2);
	EXPECT_EQ(false, date1 >= date2);
	EXPECT_EQ(false, date1 == date2);
	EXPECT_EQ(true, date1 != date2);
}

TEST(Date, formatDate)
{
	Date date;
	date = Date(11, 11, 11);
	EXPECT_EQ("11 Nov 11", date.formatDate());
	date = Date::getCurrentDate();
	EXPECT_EQ("Today", date.formatDate());
	date = Date::getCurrentDate() + 1;
	EXPECT_EQ("Tomorrow", date.formatDate());
	EXPECT_EQ("NIL", NULL_DATE.formatDate());
}

TEST(Date, addOperator)
{
	Date date(6, 6, 6);
	EXPECT_EQ("6 Jun 06", date.formatDate());
}

TEST(Time, comparisonOperators)
{
	Time time1(18, 0), time2(18, 30);
	EXPECT_EQ(true, time1 <= time1);
	EXPECT_EQ(true, time1 >= time1);
	EXPECT_EQ(true, time1 == time1);
	EXPECT_EQ(false, time2 < time2);
	EXPECT_EQ(false, time2 > time2);
	EXPECT_EQ(false, time2 != time2);

	EXPECT_EQ(true, time1 < time2);
	EXPECT_EQ(true, time1 <= time2);
	EXPECT_EQ(false, time1 > time2);
	EXPECT_EQ(false, time1 >= time2);
	EXPECT_EQ(false, time1 == time2);
	EXPECT_EQ(true, time1 != time2);
}

TEST(Time, formatTime)
{
	Time time;
	time = Time(18, 00);
	EXPECT_EQ("18:00", time.formatTime());
	time = Time(23, 59);
	EXPECT_EQ("23:59", time.formatTime());
	EXPECT_EQ("NIL", NULL_TIME.formatTime());
}

//@author A0101856J

TEST(Logic, searchtests)
{

string testUserInput = "add cooking with friends, 11 nov 2013, 12pm, 11 nov 2013, 2pm";
string testUserInput1 = "add playing with supervisors, 12 nov 2013, 5pm, 12 nov 2013, 6pm";
string testUserInput2 = "add debug all programs, 13 nov 2013";
string result;

int resultOfSearchTask;
int resultOfSearchDate;
int resultOfSearchDesc;
int resultOfSearchTime;

Date date = Date(11,11,13);
Time time = Time(12,00);

Logic::executeCommand(testUserInput,result);
Logic::executeCommand(testUserInput1,result);
Logic::executeCommand(testUserInput2,result);


resultOfSearchDate = Logic::searchByDate(date);
ASSERT_EQ(1,resultOfSearchDate);
Logic::resetBuffer();

resultOfSearchTime = Logic::searchByTime(time);
ASSERT_EQ(1, resultOfSearchTime);
Logic::resetBuffer();

resultOfSearchDesc = Logic::searchByDesc("debug");
ASSERT_EQ(1, resultOfSearchDesc);
Logic::resetBuffer();

resultOfSearchTask = Logic::searchTasks("supervisors");
EXPECT_EQ(1, resultOfSearchTask);


}



//Tests the history class for a single undo.
//Search function is used to affirm that a task has been
//added/deleted/undone successfully
//Also tests if the Logic class is able to perform addition, deletion,
//and search function properly.
TEST(History,undoSingleCommand){

string testUserInput = "add meeting with friends";
string result;
int resultOfSearchFunction;
int resultOfUndoFunction;


Logic::executeCommand(testUserInput,result);
resultOfSearchFunction= Logic::searchTasks("meeting with friends");

//ensure that task has been successfully added.
ASSERT_EQ(1,resultOfSearchFunction);

resultOfUndoFunction = History::undoPreviousCommand();
resultOfSearchFunction= Logic::searchTasks("meeting with friends");

//ensure that task can no longer be found in list.
ASSERT_EQ(0,resultOfSearchFunction);
EXPECT_EQ(1, resultOfUndoFunction);

}

//Tests the History class to perform multiple undos
//in the correct order.
//Search function used to affirm if a task has been
//added/deleted/undone successfully.
//added/deleted/undone successfully.
TEST(History, undoMultipleCommands)
{

string testUserInput = "add meeting with boss, 11 nov 2013, 12pm, 11 nov 2013, 2pm";
string testUserInput1 = "add meeting with supervisors, 12 nov 2013, 12pm, 12 nov 2013, 6pm";
string testUserInput2 = "add finish program, 13 nov 2013";
string result;
int resultOfSearchFunction;
int resultOfUndoFunction;

Logic::executeCommand(testUserInput, result);
resultOfSearchFunction = Logic::searchTasks("meeting with boss");
ASSERT_EQ(1,resultOfSearchFunction);

Logic::executeCommand(testUserInput1,result);
resultOfSearchFunction = Logic::searchTasks("meeting with supervisors");
ASSERT_EQ(1,resultOfSearchFunction);

Logic::executeCommand(testUserInput2, result);
resultOfSearchFunction = Logic::searchTasks("finish program");
ASSERT_EQ(1, resultOfSearchFunction);

resultOfUndoFunction = History::undoPreviousCommand();
ASSERT_EQ(1, resultOfUndoFunction);
resultOfSearchFunction = Logic::searchTasks("finish program");
ASSERT_EQ(0, resultOfSearchFunction);

resultOfUndoFunction = History::undoPreviousCommand();
ASSERT_EQ(1, resultOfUndoFunction);
resultOfSearchFunction = Logic::searchTasks("meeting with supervisors");
ASSERT_EQ(0, resultOfSearchFunction);

resultOfUndoFunction = History::undoPreviousCommand();
ASSERT_EQ(1, resultOfUndoFunction);
resultOfSearchFunction = Logic::searchTasks("meeting with boss");
ASSERT_EQ(0, resultOfSearchFunction);


}



//Tests the History class to perform multiple undo
// and redo in the correct order.
//Search function is used to affirm that a task
//has been added/deleted/undone successfully.
TEST(History, multipleUndoAndRedo)

{

string testUserInput = "add meeting with friends, 11 nov 2013, 12pm, 11 nov 2013, 2pm";
string testUserInput1 = "add meeting with buddies, 12 nov 2013, 12pm, 12 nov 2013, 6pm";
string testUserInput2 = "add finish popcorn, 13 nov 2013";
string result;
int resultOfSearchFunction;
int resultOfUndoFunction;
int resultOfRedoFunction;

Logic::executeCommand(testUserInput, result);
resultOfSearchFunction = Logic::searchTasks("meeting with friends");
ASSERT_EQ(1,resultOfSearchFunction);

Logic::executeCommand(testUserInput1,result);
resultOfSearchFunction = Logic::searchTasks("meeting with buddies");
ASSERT_EQ(1,resultOfSearchFunction);

Logic::executeCommand(testUserInput2, result);
resultOfSearchFunction = Logic::searchTasks("finish popcorn");
ASSERT_EQ(1, resultOfSearchFunction);

resultOfUndoFunction = History::undoPreviousCommand();
ASSERT_EQ(1, resultOfUndoFunction);
resultOfSearchFunction = Logic::searchTasks("finish popcorn");
ASSERT_EQ(0, resultOfSearchFunction);

resultOfUndoFunction = History::undoPreviousCommand();
ASSERT_EQ(1, resultOfUndoFunction);
resultOfSearchFunction = Logic::searchTasks("meeting with buddies");
ASSERT_EQ(0, resultOfSearchFunction);

resultOfUndoFunction = History::undoPreviousCommand();
ASSERT_EQ(1, resultOfUndoFunction);
resultOfSearchFunction = Logic::searchTasks("meeting with friends");
ASSERT_EQ(0, resultOfSearchFunction);

resultOfRedoFunction = History::redoPreviousCommand();
ASSERT_EQ(1, resultOfRedoFunction);
resultOfSearchFunction = Logic::searchTasks("meeting with friends");
ASSERT_EQ(1,resultOfSearchFunction);

resultOfRedoFunction = History::redoPreviousCommand();
ASSERT_EQ(1, resultOfRedoFunction);
resultOfSearchFunction = Logic::searchTasks("meeting with buddies");
ASSERT_EQ(1,resultOfSearchFunction);

resultOfRedoFunction = History::redoPreviousCommand();
ASSERT_EQ(1, resultOfRedoFunction);
resultOfSearchFunction = Logic::searchTasks("finish popcorn");
ASSERT_EQ(1,resultOfSearchFunction);


}

//Tests the undo function if it can function properly in the correct
//order while ignoring undo-able functions.
//Search function used to to affirm that a task has been added/deleted/
//undone successfully.
TEST(History, undoFunction)
{
	string testUserInput = "display";
	string testUserInput1 = "add meeting with enemies, 12 nov 2013, 12pm, 12 nov 2013, 6pm";
	string result;
	int resultOfUndoFunction;
	int resultOfSearchFunction;

	Logic::executeCommand(testUserInput1,result);
	resultOfSearchFunction = Logic::searchTasks("meeting with enemies");
	ASSERT_EQ(1, resultOfSearchFunction);

	Logic::executeCommand(testUserInput,result);
	resultOfUndoFunction = History::undoPreviousCommand();
	ASSERT_EQ(1, resultOfUndoFunction);
	
	resultOfSearchFunction = Logic::searchTasks("meeting with enemies");
	EXPECT_EQ(0, resultOfSearchFunction);

}

//Tests the redo function if it can function properly in the correct
//order while ignoring unredo-able functions.
//Search function used to affirm that a task has been added/deleted/
//redone/undone successfully.

TEST(History, redoTest)
{

	string testUserInput = "add complete project proposal, 11 nov 2013, 12pm, 11 nov 2013, 2pm";
	string testUserInput1 = "display";
	string result;
	int resultOfSearchFunction;
	int resultOfUndoFunction;
	int resultOfRedoFunction;

	Logic::executeCommand(testUserInput, result);
	resultOfSearchFunction = Logic::searchTasks("project proposal");
	ASSERT_EQ(1,resultOfSearchFunction);
	Logic::executeCommand(testUserInput1,result);
	resultOfUndoFunction = History::undoPreviousCommand();
	resultOfSearchFunction = Logic::searchTasks("project proposal");
	ASSERT_EQ(0,resultOfSearchFunction);
	Logic::executeCommand(testUserInput1,result);
	resultOfRedoFunction = History::redoPreviousCommand();
	
	resultOfSearchFunction = Logic::searchTasks("project proposal");
	ASSERT_EQ(1,resultOfSearchFunction);
	EXPECT_EQ(1,resultOfRedoFunction);
}

//User should not be able to redo what he has undone
//if he performs a new operation after the undo.
//Check if redostack is cleared after every new operation.
TEST(History,invalidRedo)
{
	string testUserInput = "add complete project report, 11 nov 2013, 12pm, 11 nov 2013, 2pm";
	string testUserInput1 = "add baking cakes with friends";
	string result;
	int resultOfSearchFunction;
	int resultOfUndoFunction;
	int resultOfRedoFunction;

	Logic::executeCommand(testUserInput,result);
	resultOfSearchFunction = Logic::searchTasks("project report");
	ASSERT_EQ(1,resultOfSearchFunction);

	//nothing to redo
	resultOfRedoFunction = History::redoPreviousCommand();
	ASSERT_EQ(0, resultOfRedoFunction);

	resultOfUndoFunction = History::undoPreviousCommand();
	ASSERT_EQ(1, resultOfUndoFunction);
	resultOfSearchFunction = Logic::searchTasks("project report");
	ASSERT_EQ(0, resultOfSearchFunction);

	//new operation, redo stack should be cleared.
	//no redo allowed.
	Logic::executeCommand(testUserInput1,result);
	resultOfSearchFunction = Logic::searchTasks("baking cakes");
	ASSERT_EQ(1,resultOfSearchFunction);

	resultOfRedoFunction = History::redoPreviousCommand();
	EXPECT_EQ(0, resultOfRedoFunction);

}



int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}